package com.sys.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @Author com.bhudy
 * @Description shiro配置
 * @Date 2019-10-30 23:09:37
 */
@Configuration
public class ShiroConfig {

//    @Value("${spring.redis.host}")
//    private String host = "127.0.0.1";
//    @Value("${spring.redis.password}")
//    private String password;
//    @Value("${spring.redis.servicePort}")
//    private Integer port = 6379;
//    @Value("${spring.redis.timeout}")
//    private Integer timeout = 0;
//
//    /**
//     * 配置shiro redisManager
//     * 使用的是shiro-redis开源插件
//     *
//     * @return
//     */
//    public RedisManager redisManager() {
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost(host);
//        redisManager.setPassword(password);
//        redisManager.setPort(port);
//        redisManager.setExpire(1800);// 配置缓存过期时间
//        redisManager.setTimeout(timeout);
//        return redisManager;
//    }
//
//
//    /**
//     * RedisSessionDAO shiro sessionDao层的实现 通过redis
//     * 使用的是shiro-redis开源插件
//     */
//    @Bean
//    public RedisSessionDAO redisSessionDAO() {
//        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
//        redisSessionDAO.setRedisManager(redisManager());
//        return redisSessionDAO;
//    }
//
//    //	配置realm，用于认证和授权
//    @Bean
//    public Realm realm(HashedCredentialsMatcher hashedCredentialsMatcher) {
//        ShiroRealm shiroRealm = new ShiroRealm();
//        //		校验密码用到的算法
//        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
//
//        return shiroRealm;
//    }
//
//    @Bean
//    public RedisCacheManager cacheManager() {
//        RedisCacheManager redisCacheManager = new RedisCacheManager();
//        redisCacheManager.setRedisManager(redisManager());
//        return redisCacheManager; //new MemoryConstrainedCacheManager();
//    }
//
//    /**
//     * cookie对象;
//     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
//     *
//     * @return
//     */
//    @Bean
//    public SimpleCookie rememberMeCookie() {
//        //System.out.println("ShiroConfiguration.rememberMeCookie()");
//        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
//        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
//        simpleCookie.setMaxAge(259200);
//        return simpleCookie;
//    }
//
//    /**
//     * cookie管理对象;
//     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
//     *
//     * @return
//     */
//    @Bean
//    public CookieRememberMeManager rememberMeManager() {
//        //System.out.println("ShiroConfiguration.rememberMeManager()");
//        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
//        cookieRememberMeManager.setCookie(rememberMeCookie());
//        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
//        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
//        return cookieRememberMeManager;
//    }
//
//    @Bean
//    public SecurityManager securityManager(Realm shiroRealm) {
//        DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
//        sm.setRealm(shiroRealm);
//        sm.setCacheManager(cacheManager());
//        //注入记住我管理器
//        sm.setRememberMeManager(rememberMeManager());
//        //注入自定义sessionManager
//        sm.setSessionManager(sessionManager());
//        // 不加这句Subjct可能会空指针
//        SecurityUtils.setSecurityManager(sm);
//
//        return sm;
//    }
//
//    //自定义sessionManager
//    @Bean
//    public SessionManager sessionManager() {
//        CustomSessionManager sessionManager = new CustomSessionManager();
//        sessionManager.setSessionDAO(redisSessionDAO());
//        //关键在这里
//        /*sessionManager.setSessionDAO(getRedisSessionDao());
//        sessionManager.setSessionValidationSchedulerEnabled(true);*/
//        /*sessionManager.setDeleteInvalidSessions(true);*/
//        return sessionManager;
//    }
//
//    public CORSAuthenticationFilter corsAuthenticationFilter() {
//        return new CORSAuthenticationFilter();
//    }
//
//    @Bean(name = "shiroFilter")
//    public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
//        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
//        shiroFilter.setSecurityManager(securityManager);
//        //SecurityUtils.setSecurityManager(securityManager);
//        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//
//        //配置不会被拦截的链接，顺序判断
//        filterChainDefinitionMap.put("/static/**", "anon");
//        filterChainDefinitionMap.put("/index.html**", "anon");
//        filterChainDefinitionMap.put("/res/**", "anon");
//        filterChainDefinitionMap.put("/jmoms/**", "anon");
//
//        filterChainDefinitionMap.put("/sysLogin", "anon");
//        filterChainDefinitionMap.put("/sysLogout", "anon");
//        filterChainDefinitionMap.put("/api/v1/sysLogout", "anon");
//        filterChainDefinitionMap.put("/api/v1/sysLogin", "anon");
//        //filterChainDefinitionMap.put("/swagger**/**", "anon");
//        //filterChainDefinitionMap.put("/swagger**/**/**", "anon");
//        //filterChainDefinitionMap.put("/swagger**", "anon");
//        filterChainDefinitionMap.put("/api/v1/wechat/**", "anon");
//        //authc:所有url必须通过认证才能访问，anon:所有url都可以匿名访问
//        filterChainDefinitionMap.put("/**", "corsAuthenticationFilter");
//        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
//        //自定义过滤器
//        Map<String, Filter> filterMap = new LinkedHashMap<>();
//        filterMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
//        shiroFilter.setFilters(filterMap);
//
//        return shiroFilter;
//    }
//
//    /**
//     * Shiro生命周期处理器 * @return
//     */
//    @Bean
//    public static LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
//        return new LifecycleBeanPostProcessor();
//    }
//
//    /**
//     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
//     */
//    @Bean
//    @DependsOn({"lifecycleBeanPostProcessor"})
//    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
//        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
//        advisorAutoProxyCreator.setProxyTargetClass(true);
//        return advisorAutoProxyCreator;
//    }
//
//    @Bean
//    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
//        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
//        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
//        return authorizationAttributeSourceAdvisor;
//    }
//
//    //  加密算法
//    @Bean
//    public HashedCredentialsMatcher hashedCredentialsMatcher() {
//        HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager());
//        hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密
//        hashedCredentialsMatcher.setHashIterations(1024);//加密次数
//        return hashedCredentialsMatcher;
//    }


    //	配置realm，用于认证和授权
    @Bean
    public Realm realm(HashedCredentialsMatcher hashedCredentialsMatcher) {
        ShiroRealm shiroRealm = new ShiroRealm();
        //		校验密码用到的算法
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);

        return shiroRealm;
    }

    @Bean
    public CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }

    /**
     * cookie对象;
     * rememberMeCookie()方法是设置Cookie的生成模版，比如cookie的name，cookie的有效时间等等。
     *
     * @return
     */
    @Bean
    public SimpleCookie rememberMeCookie() {
        //System.out.println("ShiroConfiguration.rememberMeCookie()");
        //这个参数是cookie的名称，对应前端的checkbox的name = rememberMe
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    /**
     * cookie管理对象;
     * rememberMeManager()方法是生成rememberMe管理器，而且要将这个rememberMe管理器设置到securityManager中
     *
     * @return
     */
    @Bean
    public CookieRememberMeManager rememberMeManager() {
        //System.out.println("ShiroConfiguration.rememberMeManager()");
        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie());
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }

    @Bean
    public SecurityManager securityManager(Realm shiroRealm) {
        DefaultWebSecurityManager sm = new DefaultWebSecurityManager();
        sm.setRealm(shiroRealm);
        sm.setCacheManager(cacheManager());
        //注入记住我管理器
        sm.setRememberMeManager(rememberMeManager());
        //注入自定义sessionManager
        sm.setSessionManager(sessionManager());
        // 不加这句Subjct可能会空指针
        SecurityUtils.setSecurityManager(sm);

        return sm;
    }

    //自定义sessionManager
    @Bean
    public SessionManager sessionManager() {
        CustomSessionManager sessionManager = new CustomSessionManager();
        //关键在这里
        /*sessionManager.setSessionDAO(getRedisSessionDao());
        sessionManager.setSessionValidationSchedulerEnabled(true);*/
        /*sessionManager.setDeleteInvalidSessions(true);*/
        return sessionManager;
    }

    public CORSAuthenticationFilter corsAuthenticationFilter() {
        return new CORSAuthenticationFilter();
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        //SecurityUtils.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        //配置不会被拦截的链接，顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/index.html**", "anon");
        filterChainDefinitionMap.put("/res/**", "anon");
        filterChainDefinitionMap.put("/jmoms/**", "anon");

        filterChainDefinitionMap.put("/sysLogin", "anon");
        filterChainDefinitionMap.put("/sysLogout", "anon");
        filterChainDefinitionMap.put("/api/v1/sysLogout", "anon");
        filterChainDefinitionMap.put("/api/v1/sysLogin", "anon");
        //filterChainDefinitionMap.put("/swagger**/**", "anon");
        //filterChainDefinitionMap.put("/swagger**/**/**", "anon");
        //filterChainDefinitionMap.put("/swagger**", "anon");
        filterChainDefinitionMap.put("/api/v1/wechat/**", "anon");
        //authc:所有url必须通过认证才能访问，anon:所有url都可以匿名访问
        filterChainDefinitionMap.put("/**", "corsAuthenticationFilter");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        //自定义过滤器
        Map<String, Filter> filterMap = new LinkedHashMap<>();
        filterMap.put("corsAuthenticationFilter", corsAuthenticationFilter());
        shiroFilter.setFilters(filterMap);

        return shiroFilter;
    }

    /**
     * Shiro生命周期处理器 * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能 * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    //  加密算法
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new RetryLimitHashedCredentialsMatcher(cacheManager());
        hashedCredentialsMatcher.setHashAlgorithmName("MD5");//采用MD5 进行加密
        hashedCredentialsMatcher.setHashIterations(1024);//加密次数
        return hashedCredentialsMatcher;
    }
}